/* Client Logic:
ACHTUNG: ggf. wird hier später wesentlich weniger Code zu sehen sein, das der Server mehr logic übernehmen wird.
Hier werden noch einige Anpassungen statfinden.
Zum Teil sind die Methoden nur Dummies (get und setTournament)
Um den eigenenteil zu Implementieren können alle anderen Methoden jedoch bereits genutzt werden. Nach erzeugung des Turniers empfehle ich sich das Objet das ihr dabei bekommt bei euch zu halten. noch gibt es keine Möglichkeit es zu speichern.

HOWTO:
Tunier erstellen: createTournament(Teilnehmer: StringArray) Rückgabewert: Tournament
Ergebniss eines Spieles setztes: Tournament.setErgebnis(tournament: Tournament, SpielID int, ergS1: int, ergS2: int) 
WICHTIG: nicht das Ergebnis setzten indem entsprechende Variablen eingetragen werden, sonst wird der Spielplan nicht aktualisiert!
Spiele der Gewinner bzw. Verliererrunde X abfragen: Tournament.getWinnersbracketRound(i: int, tournament: Tournament)
Rückgabewert: Array von Spielen
Spielereigenschaften können "per Hand bearbeitet werden"
*/
var socket;

$(document).ready(function () {
    socket = io('http://localhost:8080');
    socket.on('confirmConnection', confirmConnection);
});

function confirmConnection(data){
    alert(data.zeit);
    console.log(data.mensch);
}

/*
Methode legt ein neues Turnier an speichert es und gibt es zurück, wenn als bei dem zweiten Pramaeter true angegeben, wird das alte Turnier überschrieben
Input: competitors: String[] [, overrideActual: boolean]
Oupt: Tunrier: Object
*/
function createTournament(competitors, overrideActual) {
    tmp = new Tournament(competitors);
    tmp = JSON.stringify(tmp);
    //JSON ablegen (logik fehlt hierzu noch)
    return $.parseJSON(tmp);
}

/*
Methode überschreibt das aktuelle Turnier mit dem mitgegebenen.
Input: tournament: Object
Output: Void
*/
function setTournament(tournament) {
    tmp = JSON.stringify(tournament);
    //JSON ablegen
}

/*
Das aktuelle Turnier wird aus dem json-File ausgelesen und zurückgegeben.
Input: Void
Output: Turnier: Object
*/
function getTournament() {
    tmp = null; //JSON abrufen
    return $.parseJSON(tmp);
}

/*  Klasse: Player 
    Die Klasse beinhaltet zur Zeit nur den Namen des Spielers
    Über den Konstruktur wird ein Spieler angelegt.
    Input: name: String
    Output: Spieler: Player
*/

function Player(name) {
    this.name = name;
}

/* Klasse: Game
    beinhaltet folgende Attribute:
        player1: Player
        player2: player
        scorePlayer1: int
        scorePlayer2: int
        source1: Source (Herkunt von Spieler 1)
        source2: Source (Herkunft von Spieler 2)
    Über den Konstruktur wird ein neues Spiel erstellt, dabei werden die beiden Spieler mitgegeben
    Input:  player1: Spieler
            player2: Spieler
    Output: Spiel: Game
*/

function Game(player1, player2) {
        this.player1 = player1;
        this.player2 = player2;
        this.scorePlayer1 = -1;
        this.scorePlayer2 = -1;
        this.source1 = null;
        this.source2 = null;
        this.number = null;
    }
    /*
    Gibt das Ergebnis des mitgegebenen Spiels als String in folgender Form zurück: "Int : Int"
    Input: game: Game
    Output: Ergebnis: String
    */

Game.getScore = function (game) {
    return game.scorePlayer1 + " : " + game.scorePlayer2;
}

/*
Setzt die Herkunft von Spieler 1 des aktuellen Spiels (von wo kommt Spieler 1, kommt er als Verlierer oder Gewinner?) 
Input:  game: Spiel (Vorgängerspiel)
        asWinner: boolean (hat er im Vorgängerspiel verloren oder gewonnen)
Output: void
*/

Game.prototype.setSourceP1 = function (game, asWinner) {
        this.source1 = new Source(game, asWinner);
    }
    /*
    Setzt die Herkunft von Spieler 2 des aktuellen Spiels (von wo kommt Spieler 2, kommt er als Verlierer oder Gewinner?) 
    Input:  game: Spiel (Vorgängerspiel)
            asWinner: boolean (hat er im Vorgängerspiel verloren oder gewonnen)
    Output: void
    */
Game.prototype.setSourceP2 = function (game, asWinner) {
    this.source2 = new Source(game, asWinner);
}

/*
Gibt den Sieger des gegebenen Spiels zurück
Input: game: Spiel (aus dem der Gewinner ermittelt werden soll
Output: Gewinner: Player
*/
Game.getWinner = function (game) {
    if (game.player1 == -1) {
        return game.player2;
    } else if (game.player2 == -1) {
        return game.player1;
    } else {
        if (game.scorePlayer1 > game.scorePlayer2) {
            return game.player1;
        } else if (game.scorePlayer1 < game.scorePlayer2) {
            return game.player2;
        } else {
            return null;
        }
    }
}

/*
Gibt den Verlierer des gegebenen Spiels zurück
Input: game: Spiel (aus dem der Verlierer ermittelt werden soll
Output: Verlierer: Player
*/
Game.getLoser = function (game) {
    if (game.player1 == -1) {
        return game.player1;
    } else if (game.player2 == -1) {
        return game.player2;
    } else {
        if (game.scorePlayer1 < game.scorePlayer2) {
            return game.player1;
        } else if (game.scorePlayer1 > game.scorePlayer2) {
            return game.player2;
        } else {
            return null;
        }
    }
}

/* Klasse: Source 
    Die Klasse hält die Herkunft eines Spielers
    besitzt die Attribute:
    game: Game
    asWinner: boolean
Der Konstruktor legt ein neues Objekt der Klasser an.
Input: game: Game, asWinner: boolean
*/

function Source(game, asWinner) {
    this.game = game;
    this.asWinner = asWinner;
}


/* Klasse: Tournament
Erzeugt Objekte der klasse Turnier
hier wird das gesamte Turnier gehalten und initial erzeugt. Das Objekt besitzt die folgenden Attribute
    fieldSize: int (Größe des Feldes)
    gameList: Game[] (Array aller Spiele)
    competitorsList: Player[] (Array aller Teilnehmer)
    mainRound: int[]                (Spiele der Hauptrrunde mit verweis auf die Gesamtspielliste)
    winnersbracket: int[]           (Spiele der Gewinnerrunde mit verweis auf die Gesamtspielliste)
    winnersbracketRoundNumber: int  (Anzahl der Runden in der Gewinnerrunde)
    losersbracket: int[]            (Spiele der Verliererrunde mit Verweis auf die Gesamtspielliste)
    losersbracketRoundNumber: int   (Anzahl der Runden in der Verliererrunde)
    final: Spiel                    (das Finalspiel)
    counter: int                    (wird zur Zeit nicht genutzt)

Der Konstruktor erzeugt ein Objekt der Klasse
Input: competitors: Sting[] (Alle teilnehmer als String)
Output: turnier: Turnier
*/


function Tournament(competitors) {
    this.fieldSize = Math.pow(2, Math.ceil(Math.log(competitors.length) / Math.log(2)));
    this.gameList = [];
    this.competitorsList = [];
    var numberBye = this.fieldSize - competitors.length;
    for (var i = 0; i < competitors.length; i++) {
        this.competitorsList[this.competitorsList.length] = new Player(competitors[i]);
    }
    this.mainRound = [];
    this.allocationMR(numberBye);
    this.winnersbracket = [];
    this.winnersbracketRoundNumber = null;
    this.allocationWB();
    this.losersbracket = [];
    this.losersbracketRoundNumber = null;
    this.allocationLB();
    this.gameList[this.gameList.length] = new Game(null, null);
    this.final = this.gameList.length - 1;
    this.gameList[this.final].number = 1000;
    this.gameList[this.final].setSourceP1(this.winnersbracket[this.winnersbracket.length - 1], true);
    this.gameList[this.final].setSourceP2(this.losersbracket[this.losersbracket.length - 1], true);
    this.counter = 1;
    this.refreshGames();
}

Tournament.prototype.refreshGames = function () {
        for (var i = 0; i < this.gameList; i++) {
            if (this.gameList[i].source1 != null) {
                if (this.gameList[i].source1.asWinner) {
                    this.gameList[i].player1 = Game.getWinner(this.gameList[this.gameList[i].source1.game]);
                } else {
                    this.gameList[i].player1 = Game.getLoser(this.gameList[this.gameList[i].source1.game]);
                }
            }
            if (this.gameList[i].source2 != null) {
                if (this.gameList[i].source2.asWinner) {
                    this.gameList[i].player2 = Game.getWinner(this.gameList[this.gameList[i].source2.game]);
                } else {
                    this.gameList[i].player2 = Game.getLoser(this.gameList[this.gameList[i].source2.game]);
                }
            }
        }
    }
    /*
    Verteilung in der Spieler auf die Hauptrunde
    TODO: Verteilung von Freilosen
    */
Tournament.prototype.allocationMR = function (numberBye) {
    var tmpCompetitorsList = [];
    for (var i = 0; i < (this.competitorsList.length); i++) {
        tmpCompetitorsList[i] = i;
    }

    var games = []
    for (var i = 0; i < this.fieldSize / 2; i++) {
        games[i] = new Game(null, null);
        games[i].number = i + 1;
    }
    games = this.allocationBye(games, numberBye, true);

    for (var i = 0; i < games.length; i++) {
        //wenn spieler1 nicht freilos
        if (games[i].player1 != -1) {
            //setze Spieler 1
            var indexS1 = Math.floor(Math.random() * tmpCompetitorsList.length);
            games[i].player1 = tmpCompetitorsList[indexS1];
            tmpCompetitorsList.splice(indexS1, 1);
        }
        //setze Spieler 2
        var indexS2 = Math.floor(Math.random() * tmpCompetitorsList.length);
        games[i].player2 = tmpCompetitorsList[indexS2];
        tmpCompetitorsList.splice(indexS2, 1);
        //Übertragung in Spielliste und hauptrunde
        this.gameList[this.gameList.length] = games[i];
        this.mainRound[i] = this.gameList.length - 1;
    }
}

Tournament.prototype.allocationBye = function (games, numberBye, isTop) {
    if (games.length > 1 && numberBye > 0) {
        if (isTop) {
            var g1 = this.allocationBye(games.slice(0, games.length / 2), Math.ceil(numberBye / 2), true);
            var g2 = this.allocationBye(games.slice(games.length / 2, games.length), Math.floor(numberBye / 2), false);
        } else {
            var g1 = this.allocationBye(games.slice(0, games.length / 2), Math.floor(numberBye / 2), true);
            var g2 = this.allocationBye(games.slice(games.length / 2, games.length), Math.ceil(numberBye / 2), false);
        }
        games = g1.concat(g2);
    } else if (numberBye > 0) {
        games[0].player1 = -1;
        games[0].scorePlayer2 = 1;
        games[0].scorePlayer1 = 0;
    }
    return games;
}

/*
Verteilung in der Gewinnerrunde (das Setzen der Herkunft)
Wird nur intern verwendet und ist daher noch nicht weiter beschrieben
*/
Tournament.prototype.allocationWB = function () {
        for (var i = 0; i < (this.fieldSize / 2 - 1); i++) {
            this.gameList[this.gameList.length] = new Game(null, null);
            this.winnersbracket[i] = this.gameList.length - 1;
            this.gameList[this.winnersbracket[i]].number = i + 1;
        }
        this.winnersbracketRoundNumber = (Math.log((this.winnersbracket.length + 1) / 2) / Math.log(2) + 1);
        for (var i = 0; i < this.mainRound.length; i = i + 2) {
            this.gameList[this.winnersbracket[i / 2]].setSourceP1(this.mainRound[i], true);
            this.gameList[this.winnersbracket[i / 2]].setSourceP2(this.mainRound[i + 1], true);
        }
        for (var x = 1; x < this.winnersbracketRoundNumber; x++) {
            var roundX = Tournament.getWinnersbracketRound(x, this);
            var roundX1 = Tournament.getWinnersbracketRound(x + 1, this);
            for (var i = 0; i < roundX.length; i = i + 2) {
                this.gameList[roundX1[i / 2]].setSourceP1(roundX[i], true);
                this.gameList[roundX1[i / 2]].setSourceP2(roundX[i + 1], true);
            }
        }
    }
    /*
    Verteilung in der Verliererrunde (das Setzen der Herkunft)
    Wird nur intern verwendet und ist daher noch nicht weiter beschrieben
    */
Tournament.prototype.allocationLB = function () {
        for (var i = 0; i < (this.fieldSize - 2); i++) {
            this.gameList[this.gameList.length] = new Game(null, null);
            this.losersbracket[i] = this.gameList.length - 1;
            this.gameList[this.losersbracket[i]].number = i + 1;
        }
        this.losersbracketRoundNumber = Math.floor(Math.log((this.losersbracket.length / 2 + 1) / 2) / Math.log(2) + 1) * 2;
        //Verteilung HR --> VR1
        for (var i = 0; i < this.mainRound.length; i = i + 2) {
            this.gameList[this.losersbracket[i / 2]].setSourceP1(this.mainRound[i], false);
            this.gameList[this.losersbracket[i / 2]].setSourceP2(this.mainRound[i + 1], false);
        }
        //Verteilung VR/GR -- > VR
        for (var x = 1; x < this.losersbracketRoundNumber; x++) {
            if (x % 2 == 0) {
                var roundX = Tournament.getLosersbracketRound(x, this);
                var roundX1 = Tournament.getLosersbracketRound(x + 1, this);
                for (var i = 0; i < roundX.length; i = i + 2) {
                    this.gameList[roundX1[i / 2]].setSourceP1(roundX[i], true);
                    this.gameList[roundX1[i / 2]].setSourceP2(roundX[i + 1], true);
                }
            } else {
                var vrX1 = Tournament.getLosersbracketRound(x + 1, this);
                var vrX = Tournament.getLosersbracketRound(x, this);
                var gr = Tournament.getWinnersbracketRound((x + 1) / 2, this);
                for (var i = 0; i < vrX.length; i++) {
                    this.gameList[vrX1[i]].setSourceP1(vrX[i], true);
                    this.gameList[vrX1[i]].setSourceP2(gr[gr.length - 1 - i], false);
                }
            }
        }
    }
    /*
    Gibt die gewünschte Runde der Verliererrunden des angegebenen Turniers aus
    Input:  i: int (Nummer der Runde, beginned bei 1), tournament: Tournament (Turnier aus dem die Runde ausgegeben werden soll)
    Output: Spiele: Game[]
    */
Tournament.getLosersbracketRound = function (i, tournament) {
        if (i <= tournament.losersbracketRoundNumber) {
            var toGame = 0;
            var fromGame = 0;
            for (var x = 1; x <= i; x++) {
                var tmpX = Math.floor((x + 1) / 2 + 1);
                fromGame = toGame + 1;
                toGame = toGame + Math.floor(tournament.fieldSize / Math.pow(2, tmpX));
            }
            var tmp = [];
            for (var x = 0; x < (toGame - fromGame + 1); x++) {
                tmp[x] = tournament.losersbracket[x + fromGame - 1];
            }
            return tmp;
        } else {
            return null;
        }
    }
    /*
    Gibt die gewünschte Runde der Gewinnerrunden des angegebenen Turniers aus
    Input:  i: int (Nummer der Runde, beginned bei 1), tournament: Tournament (Turnier aus dem die Runde ausgegeben werden soll)
    Output: Spiele: Game[]
    */
Tournament.getWinnersbracketRound = function (i, tournament) {
        if (i <= tournament.winnersbracketRoundNumber) {
            var sumGames = Math.pow(2, tournament.winnersbracketRoundNumber - i);
            var toGame = Math.pow(2, tournament.winnersbracketRoundNumber) * (1 - Math.pow(2, -i));
            var fromGame = toGame - sumGames + 1;
            var tmp = [];
            for (var x = 0; x < sumGames; x++) {
                tmp[x] = tournament.winnersbracket[x + fromGame - 1];
            }
            return tmp;
        } else {
            return null;
        }
    }
    /*
    Setzt das Ergebnis des angegebenen Spiels im angg. Turnier
    und aktualisiert die Folgespiele.
    Input: 
        gameIndex: int (Index des Spiels in der Gesamtspielliste)
        erg1: int
        erg2: int
        tournament: Tournament
    Ouput: void
    */
Tournament.setErgebnis = function (gameIndex, erg1, erg2, tournament) {
    if (tournament.gameList[gameIndex].player1 != -1 && tournament.gameList[gameIndex].player2 != -1) {
        tournament.gameList[gameIndex].scorePlayer1 = erg1;
        tournament.gameList[gameIndex].scorePlayer2 = erg2;
        for (var i = 0; i < tournament.winnersbracket.length; i++) {
            var tmpGame = tournament.gameList[tournament.winnersbracket[i]];
            if (tmpGame.source1 != null && tmpGame.source1.game == gameIndex) {
                if (tmpGame.source1.asWinner) {
                    tmpGame.player1 = Game.getWinner(tournament.gameList[tmpGame.source1.game]);
                } else {
                    tmpGame.player1 = Game.getLoser(tournament.gameList[tmpGame.source1.game]);
                }
            }
            if (tmpGame.source2 != null && tmpGame.source2.game == gameIndex) {
                if (tmpGame.source2.asWinner) {
                    tmpGame.player2 = Game.getWinner(tournament.gameList[tmpGame.source2.game]);
                } else {
                    tmpGame.player2 = Game.getLoser(tournament.gameList[tmpGame.source2.game]);
                }
            }
        }
        for (var i = 0; i < tournament.losersbracket.length; i++) {
            var tmpGame = tournament.gameList[tournament.losersbracket[i]];
            if (tmpGame.source1 != null && tmpGame.source1.game == gameIndex) {
                if (tmpGame.source1.asWinner) {
                    tmpGame.player1 = Game.getWinner(tournament.gameList[tmpGame.source1.game]);
                } else {
                    tmpGame.player1 = Game.getLoser(tournament.gameList[tmpGame.source1.game]);
                }
            }
            if (tmpGame.source2 != null && tmpGame.source2.game == gameIndex) {
                if (tmpGame.source2.asWinner) {
                    tmpGame.player2 = Game.getWinner(tournament.gameList[tmpGame.source2.game]);
                } else {
                    tmpGame.player2 = Game.getLoser(tournament.gameList[tmpGame.source2.game]);
                }
            }
        }
    }
}


/* Alt/Testcode

$(init);

Diese methode kann gelöscht werden sobald die Eingabe der Spieler anders erfolgt

function init() {
    console.log("DOM is ready");
    $("#larsClickTest").click(btnStartTournament);
}

Diese methode kann gelöscht werden sobald die Eingabe der Spieler anders erfolgt

function btnStartTournament(){
    var stringArray = $("#tf_competitors").val();
    stringArray = stringArray.split(";");
    createTournament(stringArray);
}


function createTournament(stringArray){
    if(tournament1 == null){
        tournament1 = new Tournament(stringArray);
        Tournament.setErgebnis(tournament1, 0, 1, 3);
        Tournament.setErgebnis(tournament1, 1, 4, 3);
        Tournament.setErgebnis(tournament1, 2, 5, 6);
        Tournament.setErgebnis(tournament1, 3, 2, 0);
        tournament1JSON = JSON.stringify(tournament1);
        console.log(tournament1JSON);
        output(tournament1JSON);
    }
    else{
        alert("Es wurde bereits ein Tournament angelegt.");
    }
}


function output(jsonString){
    var tmptournament = $.parseJSON(jsonString);
    console.log(tmptournament);
    var insterion = "<h3>Hauptrunde:</h3><ul>";
    for(var i = 0; i < tmptournament.mainRound.length; i++){
        var gameIndex = tmptournament.gameList[tmptournament.mainRound[i]].number;
        if(gameIndex == null){
            gameIndex = i + 1;
        }
        insterion = insterion + "<li> Spiel Nummer: " + gameIndex + "<ul>";
        var player1 = tmptournament.competitorsList[tmptournament.gameList[tmptournament.mainRound[i]].player1].name;
        var player2 = tmptournament.competitorsList[tmptournament.gameList[tmptournament.mainRound[i]].player2].name;
        insterion = insterion + "<li>" + player1 + " vs. " + player2 + "</li>";
        var ergebnis = Spiel.getScore(tmptournament.gameList[tmptournament.mainRound[i]]);
        if(ergebnis == "-1 : -1"){
            ergebnis = "N/A";
        }
        insterion = insterion +"<li>Ergebnis: " + ergebnis + "</li>"+ "</ul></li>";
    }
    insterion = insterion + "</ul>";
    $(insterion).appendTo("#codeSpace");  
}
*/